package com.qfmy.web.app.task;

import com.alibaba.fastjson.JSON;
import com.qfmy.model.entity.novel.HotSearchRank;
import com.qfmy.model.entity.novel.SearchLog;
import com.qfmy.web.app.filter.SensitiveWordFilter;
import com.qfmy.web.app.mapper.common.HotSearchRankMapper;
import com.qfmy.web.app.mapper.common.SearchLogMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 清风明月
 * @Package com.qfmy.web.app.task
 * @date 2025/11/02
 * @description 小说的定时任务
 */
@Slf4j
@Component
public class NovelTask {

    /**
     * 搜索日志
     */
    @Autowired
    private SearchLogMapper searchLogMapper;

    /**
     * 热搜榜
     */
    @Autowired
    private HotSearchRankMapper hotSearchRankMapper;

    /**
     * RedisTemplate
     */
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 敏感词过滤器
     */
    @Autowired
    private SensitiveWordFilter sensitiveWordFilter; // 敏感词过滤器

    /**
     * 定时任务：每1小时计算一次热搜榜
     */
    @Scheduled(cron = "0 0 0/1 * * ?")
    public void calculateHotSearch() {
        // 1. 查询最近24小时的有效搜索关键词（去重）
        LocalDateTime startTime = LocalDateTime.now().minusDays(1);
        List<String> keywords = searchLogMapper.selectDistinctKeywords(startTime, LocalDateTime.now());

        // 2. 计算每个关键词的热度值
        Map<String, Integer> hotValueMap = new HashMap<>();
        for (String keyword : keywords) {
            // 过滤敏感词
            if (sensitiveWordFilter.isSensitive(keyword)) {
                continue;
            }
            // 计算热度（含时间衰减和简单防刷）
            int hotValue = calculateSingleKeywordHot(keyword, startTime);
            hotValueMap.put(keyword, hotValue);
        }

        // 3. 按热度排序，取前50名
        List<HotSearchRank> topList = hotValueMap.entrySet().stream()
                .filter(entry -> entry.getValue() > 0) // 过滤热度为0的词
                .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue())) // 降序
                .limit(50)
                .map(entry -> {
                    HotSearchRank rank = new HotSearchRank();
                    rank.setKeyword(entry.getKey());
                    rank.setHotValue(entry.getValue());
                    rank.setUpdateTime(LocalDateTime.now());
                    return rank;
                })
                .collect(Collectors.toList());
        log.info("计算热搜榜完成，结果：{}", topList);
        //判断topList是否为空
        if(topList.isEmpty()) return;

        // 4. 更新数据库和Redis缓存
        hotSearchRankMapper.batchUpdate(topList); // 批量更新到数据库
        // 缓存到Redis，方便前端查询（设置1小时过期，定时任务会刷新）
        redisTemplate.opsForValue().set(
                "hot_search:top50",
                JSON.toJSONString(topList),
                1, TimeUnit.HOURS
        );
    }

    /**
     * 计算单个关键词的热度值
     * 规则：近1小时搜索+1分，1-24小时+0.5分；同一用户10分钟内重复搜索只算1次
     */
    private int calculateSingleKeywordHot(String keyword, LocalDateTime startTime) {
        // 查询该关键词最近24小时的所有搜索记录
        List<SearchLog> logs = searchLogMapper.selectByKeywordAndTimeRange(keyword, startTime, LocalDateTime.now());
        if (logs.isEmpty()) {
            return 0;
        }

        int hotValue = 0;
        // 记录用户最近一次搜索时间（防刷：同一用户10分钟内重复搜索不计数）
        Map<Long, LocalDateTime> userLastSearchTime = new HashMap<>();

        for (SearchLog log : logs) {
            Long userId = log.getUserId();
            LocalDateTime searchTime = log.getSearchTime();

            // 防刷逻辑：同一用户10分钟内重复搜索，跳过
            if (userId != null) {
                LocalDateTime lastTime = userLastSearchTime.getOrDefault(userId, LocalDateTime.MIN);
                if (searchTime.isBefore(lastTime.plusMinutes(10))) {
                    continue;
                }
                userLastSearchTime.put(userId, searchTime);
            }

            // 时间衰减计算
            long minutes = ChronoUnit.MINUTES.between(searchTime, LocalDateTime.now());
            double score = minutes <= 60 ? 1.0 : 0.5; // 1小时内1分，1-24小时0.5分
            hotValue += (int) score;
        }
        return hotValue;
    }

}
